home *** CD-ROM | disk | FTP | other *** search
/ Programming an RTS Game with Direct3D / Programming an RTS Game with Direct3D.iso / Examples / Chapter 4 / Example 4.13 / terrain.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2006-07-01  |  15.4 KB  |  588 lines

  1. #include "terrain.h"
  2.  
  3. const DWORD TERRAINVertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX2;
  4.  
  5. //////////////////////////////////////////////////////////////////////////////////////////
  6. //                                    PATCH                                                //
  7. //////////////////////////////////////////////////////////////////////////////////////////
  8.  
  9. PATCH::PATCH()
  10. {
  11.     m_pDevice = NULL;
  12.     m_pMesh = NULL;
  13. }
  14. PATCH::~PATCH()
  15. {
  16.     Release();
  17. }
  18.  
  19. void PATCH::Release()
  20. {
  21.     if(m_pMesh != NULL)
  22.         m_pMesh->Release();
  23.     m_pMesh = NULL;
  24. }
  25.  
  26. HRESULT PATCH::CreateMesh(HEIGHTMAP &hm, RECT source, IDirect3DDevice9* Dev)
  27. {
  28.     if(m_pMesh != NULL)
  29.     {
  30.         m_pMesh->Release();
  31.         m_pMesh = NULL;
  32.     }
  33.  
  34.     try
  35.     {
  36.         m_pDevice = Dev;
  37.  
  38.         int width = source.right - source.left;
  39.         int height = source.bottom - source.top;
  40.         int nrVert = (width + 1) * (height + 1);
  41.         int nrTri = width * height * 2;
  42.  
  43.         if(FAILED(D3DXCreateMeshFVF(nrTri, nrVert, D3DXMESH_MANAGED, TERRAINVertex::FVF, m_pDevice, &m_pMesh)))
  44.         {
  45.             debug.Print("Couldn't create mesh for PATCH");
  46.             return E_FAIL;
  47.         }
  48.  
  49.         //Create vertices
  50.         TERRAINVertex* ver = 0;
  51.         m_pMesh->LockVertexBuffer(0,(void**)&ver);
  52.         for(int z=source.top, z0 = 0;z<=source.bottom;z++, z0++)
  53.             for(int x=source.left, x0 = 0;x<=source.right;x++, x0++)
  54.             {
  55.                 D3DXVECTOR3 pos = D3DXVECTOR3(x, hm.m_pHeightMap[x + z * hm.m_size.x], -z);
  56.                 D3DXVECTOR2 alphaUV = D3DXVECTOR2(x / (float)hm.m_size.x, z / (float)hm.m_size.y);        //Alpha UV
  57.                 D3DXVECTOR2 colorUV = alphaUV * 8.0f;                                                    //Color UV
  58.                 ver[z0 * (width + 1) + x0] = TERRAINVertex(pos, alphaUV, colorUV);
  59.             }
  60.         m_pMesh->UnlockVertexBuffer();
  61.  
  62.         //Calculate Indices
  63.         WORD* ind = 0;
  64.         m_pMesh->LockIndexBuffer(0,(void**)&ind);    
  65.         int index = 0;
  66.  
  67.         for(int z=source.top, z0 = 0;z<source.bottom;z++, z0++)
  68.             for(int x=source.left, x0 = 0;x<source.right;x++, x0++)
  69.             {
  70.                 //Triangle 1
  71.                 ind[index++] =   z0   * (width + 1) + x0;
  72.                 ind[index++] =   z0   * (width + 1) + x0 + 1;
  73.                 ind[index++] = (z0+1) * (width + 1) + x0;        
  74.  
  75.                 //Triangle 2
  76.                 ind[index++] = (z0+1) * (width + 1) + x0;
  77.                 ind[index++] =   z0   * (width + 1) + x0 + 1;
  78.                 ind[index++] = (z0+1) * (width + 1) + x0 + 1;
  79.             }
  80.  
  81.         m_pMesh->UnlockIndexBuffer();
  82.  
  83.         //Set Attributes
  84.         DWORD *att = 0, a = 0;
  85.         m_pMesh->LockAttributeBuffer(0,&att);
  86.         memset(att, 0, sizeof(DWORD)*nrTri);
  87.         m_pMesh->UnlockAttributeBuffer();
  88.  
  89.         //Compute normals
  90.         D3DXComputeNormals(m_pMesh, NULL);
  91.     }
  92.     catch(...)
  93.     {
  94.         debug.Print("Error in PATCH::CreateMesh()");
  95.         return E_FAIL;
  96.     }
  97.  
  98.     return S_OK;
  99. }
  100.  
  101. void PATCH::Render()
  102. {
  103.     //Draw mesh
  104.     if(m_pMesh != NULL)
  105.         m_pMesh->DrawSubset(0);
  106. }
  107.  
  108. //////////////////////////////////////////////////////////////////////////////////////////
  109. //                                    TERRAIN                                                //
  110. //////////////////////////////////////////////////////////////////////////////////////////
  111.  
  112. TERRAIN::TERRAIN()
  113. {
  114.     m_pDevice = NULL;
  115.     m_pMapTiles = NULL;
  116. }
  117.  
  118. void TERRAIN::Init(IDirect3DDevice9* Dev, INTPOINT _size)
  119. {
  120.     m_pDevice = Dev;
  121.     m_size = _size;
  122.     m_pHeightMap = NULL;
  123.  
  124.     if(m_pMapTiles != NULL)    //Clear old maptiles
  125.         delete [] m_pMapTiles;
  126.  
  127.     //Create maptiles
  128.     m_pMapTiles = new MAPTILE[m_size.x * m_size.y];
  129.     memset(m_pMapTiles, 0, sizeof(MAPTILE)*m_size.x*m_size.y);
  130.  
  131.     //Load textures
  132.     IDirect3DTexture9* grass = NULL, *mount = NULL, *snow = NULL;
  133.     if(FAILED(D3DXCreateTextureFromFile(Dev, "textures/grass.jpg", &grass)))debug.Print("Could not load grass.jpg");
  134.     if(FAILED(D3DXCreateTextureFromFile(Dev, "textures/mountain.jpg", &mount)))debug.Print("Could not load mountain.jpg");
  135.     if(FAILED(D3DXCreateTextureFromFile(Dev, "textures/snow.jpg", &snow)))debug.Print("Could not load snow.jpg");
  136.     m_diffuseMaps.push_back(grass);
  137.     m_diffuseMaps.push_back(mount);
  138.     m_diffuseMaps.push_back(snow);
  139.     m_pAlphaMap = NULL;
  140.  
  141.     //Load pixelshader
  142.     m_terrainPS.Init(Dev, "Shaders/terrain.ps", PIXEL_SHADER);
  143.  
  144.     //Create white material    
  145.     m_mtrl.Ambient = m_mtrl.Specular = m_mtrl.Diffuse  = D3DXCOLOR(0.5f, 0.5f, 0.5f, 1.0f);
  146.     m_mtrl.Emissive = D3DXCOLOR(0.0f, 0.0f, 0.0f, 1.0f);
  147.  
  148.     GenerateRandomTerrain(3);
  149. }
  150.  
  151. void TERRAIN::Release()
  152. {
  153.     for(int i=0;i<m_patches.size();i++)
  154.         if(m_patches[i] != NULL)
  155.             m_patches[i]->Release();
  156.  
  157.     m_patches.clear();
  158.  
  159.     if(m_pHeightMap != NULL)
  160.     {
  161.         m_pHeightMap->Release();
  162.         delete m_pHeightMap;
  163.         m_pHeightMap = NULL;
  164.     }
  165.  
  166.     m_objects.clear();
  167. }
  168.  
  169. void TERRAIN::GenerateRandomTerrain(int numPatches)
  170. {
  171.     try
  172.     {
  173.         Release();
  174.  
  175.         //Create two heightmaps and multiply them
  176.         m_pHeightMap = new HEIGHTMAP(m_size, 20.0f);
  177.         HEIGHTMAP hm2(m_size, 2.0f);
  178.  
  179.         m_pHeightMap->CreateRandomHeightMap(rand()%2000, 2.0f, 0.5f, 8);
  180.         hm2.CreateRandomHeightMap(rand()%2000, 2.5f, 0.8f, 3);
  181.  
  182.         hm2.Cap(hm2.m_fMaxHeight * 0.4f);
  183.  
  184.         *m_pHeightMap *= hm2;
  185.         hm2.Release();
  186.         
  187.         //Add objects
  188.         HEIGHTMAP hm3(m_size, 1.0f);
  189.         hm3.CreateRandomHeightMap(rand()%1000, 5.5f, 0.9f, 7);
  190.  
  191.         for(int y=0;y<m_size.y;y++)
  192.             for(int x=0;x<m_size.x;x++)
  193.             {
  194.                 if(m_pHeightMap->GetHeight(x, y) == 0.0f && hm3.GetHeight(x, y) > 0.7f && rand()%6 == 0)
  195.                     AddObject(0, INTPOINT(x, y));    //Tree
  196.                 else if(m_pHeightMap->GetHeight(x, y) >= 1.0f && hm3.GetHeight(x, y) > 0.9f && rand()%20 == 0)
  197.                     AddObject(1, INTPOINT(x, y));    //Stone
  198.             }
  199.  
  200.         hm3.Release();
  201.  
  202.         InitPathfinding();
  203.         CreatePatches(numPatches);
  204.         CalculateAlphaMaps();
  205.     }
  206.     catch(...)
  207.     {
  208.         debug.Print("Error in TERRAIN::GenerateRandomTerrain()");
  209.     }
  210. }
  211.  
  212. void TERRAIN::CreatePatches(int numPatches)
  213. {
  214.     try
  215.     {
  216.         //Clear any old patches
  217.         for(int i=0;i<m_patches.size();i++)
  218.             if(m_patches[i] != NULL)
  219.                 m_patches[i]->Release();
  220.         m_patches.clear();
  221.  
  222.         if(m_pHeightMap == NULL)return;
  223.  
  224.         //Create new m_patches
  225.         for(int y=0;y<numPatches;y++)
  226.             for(int x=0;x<numPatches;x++)
  227.             {
  228.                 RECT r = {x * (m_size.x - 1) / (float)numPatches, 
  229.                           y * (m_size.y - 1) / (float)numPatches, 
  230.                         (x+1) * (m_size.x - 1) / (float)numPatches,
  231.                         (y+1) * (m_size.y - 1) / (float)numPatches};
  232.                         
  233.                 PATCH *p = new PATCH();
  234.                 p->CreateMesh(*m_pHeightMap, r, m_pDevice);
  235.                 m_patches.push_back(p);
  236.             }
  237.     }
  238.     catch(...)
  239.     {
  240.         debug.Print("Error in TERRAIN::CreatePatches()");
  241.     }
  242. }
  243.  
  244. void TERRAIN::CalculateAlphaMaps()
  245. {
  246.     //Clear old alpha maps
  247.     if(m_pAlphaMap != NULL)
  248.         m_pAlphaMap->Release();
  249.  
  250.     //Create new alpha map
  251.     D3DXCreateTexture(m_pDevice, 128, 128, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &m_pAlphaMap);
  252.  
  253.     //Lock the texture
  254.     D3DLOCKED_RECT sRect;
  255.     m_pAlphaMap->LockRect(0, &sRect, NULL, NULL);
  256.     BYTE *bytes = (BYTE*)sRect.pBits;
  257.     memset(bytes, 0, 128*sRect.Pitch);        //Clear texture to black
  258.  
  259.     for(int i=0;i<m_diffuseMaps.size();i++)
  260.         for(int y=0;y<sRect.Pitch / 4;y++)
  261.             for(int x=0;x<sRect.Pitch / 4;x++)
  262.             {
  263.                 int terrain_x = m_size.x * (x / (float)(sRect.Pitch / 4.0f));
  264.                 int terrain_y = m_size.y * (y / (float)(sRect.Pitch / 4.0f));
  265.                 MAPTILE *tile = GetTile(terrain_x, terrain_y);
  266.  
  267.                 if(tile != NULL && tile->m_type == i)
  268.                     bytes[y * sRect.Pitch + x * 4 + i] = 255;
  269.             }
  270.  
  271.     //Unlock the texture
  272.     m_pAlphaMap->UnlockRect(0);
  273.     
  274.     //D3DXSaveTextureToFile("alpha.bmp", D3DXIFF_BMP, m_pAlphaMap, NULL);
  275. }
  276.  
  277. void TERRAIN::AddObject(int type, INTPOINT mappos)
  278. {
  279.     D3DXVECTOR3 pos = D3DXVECTOR3(mappos.x, m_pHeightMap->GetHeight(mappos), -mappos.y);    
  280.     D3DXVECTOR3 rot = D3DXVECTOR3((rand()%1000 / 1000.0f) * 0.13f, (rand()%1000 / 1000.0f) * 3.0f, (rand()%1000 / 1000.0f) * 0.13);
  281.  
  282.     float sca_xz = (rand()%1000 / 1000.0f) * 0.5f + 0.5f;
  283.     float sca_y = (rand()%1000 / 1000.0f) * 1.0f + 0.5f;
  284.     D3DXVECTOR3 sca = D3DXVECTOR3(sca_xz, sca_y, sca_xz);
  285.  
  286.     m_objects.push_back(OBJECT(type, mappos, pos, rot, sca));
  287. }
  288.  
  289. void TERRAIN::Render()
  290. {
  291.     //Set render states        
  292.     m_pDevice->SetRenderState(D3DRS_LIGHTING, false);
  293.     m_pDevice->SetRenderState(D3DRS_ZWRITEENABLE, true);    
  294.     
  295.     m_pDevice->SetTexture(0, m_pAlphaMap);
  296.     m_pDevice->SetTexture(1, m_diffuseMaps[0]);        //Grass
  297.     m_pDevice->SetTexture(2, m_diffuseMaps[1]);        //Mountain
  298.     m_pDevice->SetTexture(3, m_diffuseMaps[2]);        //Snow
  299.     m_pDevice->SetMaterial(&m_mtrl);
  300.  
  301.     m_terrainPS.Begin();
  302.         
  303.     for(int p=0;p<m_patches.size();p++)
  304.         m_patches[p]->Render();
  305.  
  306.     m_terrainPS.End();    
  307.  
  308.     //Render Objects
  309.     for(int i=0;i<m_objects.size();i++)
  310.         m_objects[i].Render();
  311. }
  312.  
  313. bool TERRAIN::Within(INTPOINT p)
  314. {
  315.     return p.x >= 0 && p.y >= 0 && p.x < m_size.x && p.y < m_size.y;
  316. }
  317.  
  318. void TERRAIN::InitPathfinding()
  319. {
  320.     try
  321.     {
  322.         //Read maptile heights & types from heightmap
  323.         for(int y=0;y<m_size.y;y++)
  324.             for(int x=0;x<m_size.x;x++)
  325.             {
  326.                 MAPTILE *tile = GetTile(x, y);
  327.                 tile->m_height = m_pHeightMap->GetHeight(x, y);
  328.                 tile->m_mappos = INTPOINT(x, y);
  329.                 
  330.                 if(tile->m_height < 1.0f)tile->m_type = 0;            //Grass
  331.                 else if(tile->m_height < 15.0f)tile->m_type = 1;    //Stone
  332.                 else tile->m_type = 2;                                //Snow
  333.             }
  334.  
  335.         //Calculate tile cost as a function of the height variance
  336.         for(int y=0;y<m_size.y;y++)        
  337.             for(int x=0;x<m_size.x;x++)
  338.             {
  339.                 MAPTILE *tile = GetTile(x, y);
  340.  
  341.                 if(tile != NULL)
  342.                 {
  343.                     //Possible neighbors
  344.                     INTPOINT p[] = {INTPOINT(x-1, y-1), INTPOINT(x, y-1), INTPOINT(x+1, y-1),
  345.                                     INTPOINT(x-1, y),                      INTPOINT(x+1, y),
  346.                                     INTPOINT(x-1, y+1), INTPOINT(x, y+1), INTPOINT(x+1, y+1)};
  347.  
  348.                     float variance = 0.0f;
  349.                     int nr = 0;
  350.  
  351.                     //For each neighbor
  352.                     for(int i=0;i<8;i++)    
  353.                         if(Within(p[i]))
  354.                         {
  355.                             MAPTILE *neighbor = GetTile(p[i]);
  356.  
  357.                             if(neighbor != NULL)
  358.                             {
  359.                                 float v = neighbor->m_height - tile->m_height;
  360.                                 variance += (v * v);
  361.                                 nr++;
  362.                             }
  363.                         }
  364.  
  365.                     //Cost = height variance
  366.                     variance /= (float)nr;
  367.                     tile->m_cost = variance + 0.1f;
  368.                     if(tile->m_cost > 1.0f)tile->m_cost = 1.0f;
  369.  
  370.                     //If the tile cost is less than 1.0f, then we can walk on the tile
  371.                     tile->m_walkable = tile->m_cost < 0.5f;
  372.                 }
  373.             }
  374.  
  375.         //Make maptiles with objects on them not walkable
  376.         for(int i=0;i<m_objects.size();i++)
  377.         {
  378.             MAPTILE *tile = GetTile(m_objects[i].m_mappos);
  379.             if(tile != NULL)
  380.             {
  381.                 tile->m_walkable = false;
  382.                 tile->m_cost = 1.0f;
  383.             }
  384.         }
  385.  
  386.         //Connect maptiles using the neightbors[] pointers
  387.         for(int y=0;y<m_size.y;y++)        
  388.             for(int x=0;x<m_size.x;x++)
  389.             {
  390.                 MAPTILE *tile = GetTile(x, y);
  391.                 if(tile != NULL && tile->m_walkable)
  392.                 {
  393.                     //Clear old connections
  394.                     for(int i=0;i<8;i++)
  395.                         tile->m_pNeighbors[i] = NULL;
  396.  
  397.                     //Possible neighbors
  398.                     INTPOINT p[] = {INTPOINT(x-1, y-1), INTPOINT(x, y-1), INTPOINT(x+1, y-1),
  399.                                     INTPOINT(x-1, y),                      INTPOINT(x+1, y),
  400.                                     INTPOINT(x-1, y+1), INTPOINT(x, y+1), INTPOINT(x+1, y+1)};
  401.  
  402.                     //For each neighbor
  403.                     for(int i=0;i<8;i++)    
  404.                         if(Within(p[i]))
  405.                         {
  406.                             MAPTILE *neighbor = GetTile(p[i]);
  407.  
  408.                             //Connect tiles if the neighbor is walkable
  409.                             if(neighbor != NULL && neighbor->m_walkable)
  410.                                 tile->m_pNeighbors[i] = neighbor;
  411.                         }
  412.                 }
  413.             }
  414.  
  415.         CreateTileSets();
  416.     }
  417.     catch(...)
  418.     {
  419.         debug.Print("Error in InitPathfinding()");
  420.     }    
  421. }
  422.  
  423. void TERRAIN::CreateTileSets()
  424. {
  425.     try
  426.     {
  427.         int setNo = 0;
  428.         for(int y=0;y<m_size.y;y++)        //Set a unique set for each tile...
  429.             for(int x=0;x<m_size.x;x++)
  430.                 m_pMapTiles[x + y * m_size.x].m_set = setNo++;
  431.  
  432.         bool changed = true;
  433.         while(changed)
  434.         {
  435.             changed = false;
  436.  
  437.             for(int y=0;y<m_size.y;y++)
  438.                 for(int x=0;x<m_size.x;x++)
  439.                 {
  440.                     MAPTILE *tile = GetTile(x, y);
  441.  
  442.                     //Find the lowest set of a neighbor
  443.                     if(tile != NULL && tile->m_walkable)
  444.                     {
  445.                         for(int i=0;i<8;i++)
  446.                             if(tile->m_pNeighbors[i] != NULL &&
  447.                                 tile->m_pNeighbors[i]->m_walkable &&
  448.                                 tile->m_pNeighbors[i]->m_set < tile->m_set)
  449.                             {
  450.                                 changed = true;
  451.                                 tile->m_set = tile->m_pNeighbors[i]->m_set;
  452.                             }
  453.                     }
  454.                 }
  455.         }
  456.     }
  457.     catch(...)
  458.     {
  459.         debug.Print("Error in TERRAIN::CreateTileSets()");
  460.     }
  461. }
  462.  
  463. float H(INTPOINT a, INTPOINT b)
  464. {
  465.     //return abs(a.x - b.x) + abs(a.y - b.y);
  466.     return a.Distance(b);
  467. }
  468.  
  469. std::vector<INTPOINT> TERRAIN::GetPath(INTPOINT start, INTPOINT goal)
  470. {
  471.     try
  472.     {
  473.         //Check that the two points are within the bounds of the map
  474.         MAPTILE *startTile = GetTile(start);
  475.         MAPTILE *goalTile = GetTile(goal);
  476.  
  477.         if(!Within(start) || !Within(goal) || start == goal || startTile == NULL || goalTile == NULL)
  478.             return std::vector<INTPOINT>();
  479.  
  480.         //Check if a path exists
  481.         if(!startTile->m_walkable || !goalTile->m_walkable || startTile->m_set != goalTile->m_set)
  482.             return std::vector<INTPOINT>();
  483.  
  484.         //Init Search
  485.         long numTiles = m_size.x * m_size.y;
  486.         for(long l=0;l<numTiles;l++)
  487.         {
  488.             m_pMapTiles[l].f = m_pMapTiles[l].g = INT_MAX;            //Clear F,G
  489.             m_pMapTiles[l].open = m_pMapTiles[l].closed = false;    //Reset Open and Closed
  490.         }
  491.  
  492.         std::vector<MAPTILE*> open;                //Create Our Open list
  493.         startTile->g = 0;                        //Init our starting point (SP)
  494.         startTile->f = H(start, goal);
  495.         startTile->open = true;
  496.         open.push_back(startTile);                //Add SP to the Open list
  497.  
  498.         bool found = false;                     // Search as long as a path hasnt been found,
  499.         while(!found && !open.empty())         // or there is no more tiles to search
  500.         {                                                
  501.             MAPTILE * best = open[0];        // Find the best tile (i.e. the lowest F value)
  502.             int bestPlace = 0;
  503.             for(int i=1;i<open.size();i++)
  504.                 if(open[i]->f < best->f)
  505.                 {
  506.                     best = open[i];
  507.                     bestPlace = i;
  508.                 }
  509.             
  510.             if(best == NULL)break;            //No path found
  511.  
  512.             open[bestPlace]->open = false;
  513.             open.erase(&open[bestPlace]);    // Take the best node out of the Open list
  514.  
  515.             if(best->m_mappos == goal)        //If the goal has been found
  516.             {
  517.                 std::vector<INTPOINT> p, p2;
  518.                 MAPTILE *point = best;
  519.  
  520.                 while(point->m_mappos != start)    // Generate path
  521.                 {
  522.                     p.push_back(point->m_mappos);
  523.                     point = point->m_pParent;
  524.                 }
  525.  
  526.                 for(int i=p.size()-1;i!=0;i--)    // Reverse path
  527.                     p2.push_back(p[i]);
  528.                 p2.push_back(goal);
  529.                 return p2;
  530.             }
  531.             else
  532.             {
  533.                 for(i=0;i<8;i++)                        // otherwise, check the neighbors of the
  534.                     if(best->m_pNeighbors[i] != NULL)    // best tile
  535.                     {
  536.                         bool inList = false;            // Generate new G and F value
  537.                         float newG = best->g + 1.0f;
  538.                         float d = H(best->m_mappos, best->m_pNeighbors[i]->m_mappos);
  539.                         float newF = newG + H(best->m_pNeighbors[i]->m_mappos, goal) + best->m_pNeighbors[i]->m_cost * 5.0f * d;
  540.  
  541.                         if(best->m_pNeighbors[i]->open || best->m_pNeighbors[i]->closed)
  542.                         {
  543.                             if(newF < best->m_pNeighbors[i]->f)        // If the new F value is lower
  544.                             {
  545.                                 best->m_pNeighbors[i]->g = newG;    // update the values of this tile
  546.                                 best->m_pNeighbors[i]->f = newF;
  547.                                 best->m_pNeighbors[i]->m_pParent = best;                                
  548.                             }
  549.                             inList = true;
  550.                         }
  551.  
  552.                         if(!inList)            // If the neighbor tile isn't in the Open or Closed list
  553.                         {
  554.                             best->m_pNeighbors[i]->f = newF;        //Set the values
  555.                             best->m_pNeighbors[i]->g = newG;
  556.                             best->m_pNeighbors[i]->m_pParent = best;
  557.                             best->m_pNeighbors[i]->open = true;
  558.                             open.push_back(best->m_pNeighbors[i]);    //Add it to the open list    
  559.                         }
  560.                     }
  561.  
  562.                 best->closed = true;        //The best tile has now been searched, add it to the Closed list
  563.             }
  564.         }
  565.  
  566.         return std::vector<INTPOINT>();        //No path found, return an empty path
  567.         
  568.     }
  569.     catch(...)
  570.     {
  571.         debug.Print("Error in TERRAIN::GetPath()");
  572.         return std::vector<INTPOINT>();
  573.     }
  574. }
  575.  
  576. MAPTILE* TERRAIN::GetTile(int x, int y)
  577. {
  578.     if(m_pMapTiles == NULL)return NULL;
  579.  
  580.     try
  581.     {
  582.         return &m_pMapTiles[x + y * m_size.x];
  583.     }
  584.     catch(...)
  585.     {
  586.         return NULL;
  587.     }
  588. }